// Decompiled on 周六 2月 22 19:31:49 CST 2025 with Zomboid Decompiler v0.1.3 using Vineflower.
package zombie.iso;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Stack;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import zombie.characters.IsoPlayer;
import zombie.core.Core;
import zombie.core.ThreadGroups;
import zombie.core.Translator;
import zombie.core.network.ByteBufferWriter;
import zombie.core.raknet.UdpConnection;
import zombie.debug.DebugLog;
import zombie.debug.DebugOptions;
import zombie.debug.DebugType;
import zombie.erosion.categories.ErosionCategory;
import zombie.gameStates.GameLoadingState;
import zombie.network.ChunkChecksum;
import zombie.network.GameClient;
import zombie.network.GameServer;
import zombie.network.MPStatistics;
import zombie.network.PacketTypes;
import zombie.savefile.PlayerDB;
import zombie.vehicles.VehiclesDB2;

public final class WorldStreamer {
    static final WorldStreamer.ChunkComparator comp = new WorldStreamer.ChunkComparator();
    private static final int CRF_CANCEL = 1;
    private static final int CRF_CANCEL_SENT = 2;
    private static final int CRF_DELETE = 4;
    private static final int CRF_TIMEOUT = 8;
    private static final int CRF_RECEIVED = 16;
    private static final int BLOCK_SIZE = 1024;
    public static WorldStreamer instance = new WorldStreamer();
    private final ConcurrentLinkedQueue<IsoChunk> jobQueue = new ConcurrentLinkedQueue();
    private final Stack<IsoChunk> jobList = new Stack();
    private final ConcurrentLinkedQueue<IsoChunk> chunkRequests0 = new ConcurrentLinkedQueue();
    private final ArrayList<IsoChunk> chunkRequests1 = new ArrayList();
    private final ArrayList<WorldStreamer.ChunkRequest> pendingRequests = new ArrayList();
    private final ArrayList<WorldStreamer.ChunkRequest> pendingRequests1 = new ArrayList();
    private final ConcurrentLinkedQueue<WorldStreamer.ChunkRequest> sentRequests = new ConcurrentLinkedQueue();
    private final CRC32 crc32 = new CRC32();
    private final ConcurrentLinkedQueue<ByteBuffer> freeBuffers = new ConcurrentLinkedQueue();
    private final ConcurrentLinkedQueue<WorldStreamer.ChunkRequest> waitingToSendQ = new ConcurrentLinkedQueue();
    private final ArrayList<WorldStreamer.ChunkRequest> tempRequests = new ArrayList();
    private final Inflater decompressor = new Inflater();
    private final byte[] readBuf = new byte[1024];
    private final ConcurrentLinkedQueue<WorldStreamer.ChunkRequest> waitingToCancelQ = new ConcurrentLinkedQueue();
    public Thread worldStreamer;
    public boolean bFinished = false;
    private IsoChunk chunkHeadMain;
    private int requestNumber;
    private boolean bCompare = false;
    private boolean NetworkFileDebug;
    private ByteBuffer inMemoryZip;
    private boolean requestingLargeArea = false;
    private volatile int largeAreaDownloads;
    private ByteBuffer bb1 = ByteBuffer.allocate(5120);
    private ByteBuffer bb2 = ByteBuffer.allocate(5120);

    private int bufferSize(int _int) {
        return (_int + 1024 - 1) / 1024 * 1024;
    }

    private ByteBuffer ensureCapacity(ByteBuffer byteBuffer0, int _int) {
        if (byteBuffer0 == null) {
            return ByteBuffer.allocate(this.bufferSize(_int));
        } else if (byteBuffer0.capacity() < _int) {
            ByteBuffer byteBuffer1 = ByteBuffer.allocate(this.bufferSize(_int));
            return byteBuffer1.put(byteBuffer0.array(), 0, byteBuffer0.position());
        } else {
            return byteBuffer0;
        }
    }

    private ByteBuffer getByteBuffer(int _int) {
        ByteBuffer byteBuffer = (ByteBuffer)this.freeBuffers.poll();
        if (byteBuffer == null) {
            return ByteBuffer.allocate(this.bufferSize(_int));
        } else {
            byteBuffer.clear();
            return this.ensureCapacity(byteBuffer, _int);
        }
    }

    private void releaseBuffer(ByteBuffer byteBuffer) {
        this.freeBuffers.add(byteBuffer);
    }

    private void sendRequests() throws IOException {
        if (!this.chunkRequests1.isEmpty()) {
            if (!this.requestingLargeArea || this.pendingRequests1.size() <= 20) {
                long _long = System.currentTimeMillis();
                WorldStreamer.ChunkRequest chunkRequest0 = null;
                WorldStreamer.ChunkRequest chunkRequest1 = null;

                for (int _int = this.chunkRequests1.size() - 1; _int >= 0; _int--) {
                    IsoChunk isoChunk = (IsoChunk)this.chunkRequests1.get(_int);
                    WorldStreamer.ChunkRequest chunkRequest2 = WorldStreamer.ChunkRequest.alloc();
                    chunkRequest2.chunk = isoChunk;
                    chunkRequest2.requestNumber = this.requestNumber++;
                    chunkRequest2.time = _long;
                    chunkRequest2.crc = ChunkChecksum.getChecksum(isoChunk.wx, isoChunk.wy);
                    if (chunkRequest0 == null) {
                        chunkRequest0 = chunkRequest2;
                    } else {
                        chunkRequest1.next = chunkRequest2;
                    }

                    chunkRequest2.next = null;
                    chunkRequest1 = chunkRequest2;
                    this.pendingRequests1.add(chunkRequest2);
                    this.chunkRequests1.remove(_int);
                    if (this.requestingLargeArea && this.pendingRequests1.size() >= 40) {
                        break;
                    }
                }

                this.waitingToSendQ.add(chunkRequest0);
            }
        }
    }

    public void updateMain() {
        UdpConnection udpConnection = GameClient.connection;
        if (this.chunkHeadMain != null) {
            this.chunkRequests0.add(this.chunkHeadMain);
            this.chunkHeadMain = null;
        }

        this.tempRequests.clear();

        for (WorldStreamer.ChunkRequest chunkRequest0 = (WorldStreamer.ChunkRequest)this.waitingToSendQ.poll();
            chunkRequest0 != null;
            chunkRequest0 = (WorldStreamer.ChunkRequest)this.waitingToSendQ.poll()
        ) {
            while (chunkRequest0 != null) {
                WorldStreamer.ChunkRequest chunkRequest1 = chunkRequest0.next;
                if ((chunkRequest0.flagsWS & 1) != 0) {
                    chunkRequest0.flagsUDP |= 16;
                } else {
                    this.tempRequests.add(chunkRequest0);
                }

                chunkRequest0 = chunkRequest1;
            }
        }

        if (!this.tempRequests.isEmpty()) {
            ByteBufferWriter byteBufferWriter0 = udpConnection.startPacket();
            PacketTypes.PacketType.RequestZipList.doPacket(byteBufferWriter0);
            byteBufferWriter0.putInt(this.tempRequests.size());

            for (int int0 = 0; int0 < this.tempRequests.size(); int0++) {
                WorldStreamer.ChunkRequest chunkRequest2 = (WorldStreamer.ChunkRequest)this.tempRequests.get(int0);
                byteBufferWriter0.putInt(chunkRequest2.requestNumber);
                byteBufferWriter0.putInt(chunkRequest2.chunk.wx);
                byteBufferWriter0.putInt(chunkRequest2.chunk.wy);
                byteBufferWriter0.putLong(chunkRequest2.crc);
                if (this.NetworkFileDebug) {
                    DebugLog.log(DebugType.NetworkFileDebug, "requested " + chunkRequest2.chunk.wx + "," + chunkRequest2.chunk.wy + " crc=" + chunkRequest2.crc);
                }
            }

            PacketTypes.PacketType.RequestZipList.send(udpConnection);

            for (int int1 = 0; int1 < this.tempRequests.size(); int1++) {
                WorldStreamer.ChunkRequest chunkRequest3 = (WorldStreamer.ChunkRequest)this.tempRequests.get(int1);
                this.sentRequests.add(chunkRequest3);
            }
        }

        this.tempRequests.clear();

        for (WorldStreamer.ChunkRequest chunkRequest4 = (WorldStreamer.ChunkRequest)this.waitingToCancelQ.poll();
            chunkRequest4 != null;
            chunkRequest4 = (WorldStreamer.ChunkRequest)this.waitingToCancelQ.poll()
        ) {
            this.tempRequests.add(chunkRequest4);
        }

        if (!this.tempRequests.isEmpty()) {
            ByteBufferWriter byteBufferWriter1 = udpConnection.startPacket();
            PacketTypes.PacketType.NotRequiredInZip.doPacket(byteBufferWriter1);

            try {
                byteBufferWriter1.putInt(this.tempRequests.size());

                for (int int2 = 0; int2 < this.tempRequests.size(); int2++) {
                    WorldStreamer.ChunkRequest chunkRequest5 = (WorldStreamer.ChunkRequest)this.tempRequests.get(int2);
                    if (this.NetworkFileDebug) {
                        DebugLog.log(DebugType.NetworkFileDebug, "cancelled " + chunkRequest5.chunk.wx + "," + chunkRequest5.chunk.wy);
                    }

                    byteBufferWriter1.putInt(chunkRequest5.requestNumber);
                    chunkRequest5.flagsMain |= 2;
                }

                PacketTypes.PacketType.NotRequiredInZip.send(udpConnection);
            } catch (Exception exception) {
                exception.printStackTrace();
                udpConnection.cancelPacket();
            }
        }
    }

    public void getStatistics() {
        MPStatistics.countChunkRequests(
            this.sentRequests.size(), this.chunkRequests0.size(), this.chunkRequests1.size(), this.pendingRequests.size(), this.pendingRequests1.size()
        );
    }

    private void loadReceivedChunks() throws DataFormatException, IOException {
        boolean _boolean = false;
        int int0 = 0;
        int int1 = 0;

        for (int int2 = 0; int2 < this.pendingRequests1.size(); int2++) {
            WorldStreamer.ChunkRequest chunkRequest = (WorldStreamer.ChunkRequest)this.pendingRequests1.get(int2);
            if ((chunkRequest.flagsUDP & 16) != 0) {
                if (_boolean) {
                    int0++;
                    if ((chunkRequest.flagsWS & 1) != 0) {
                        int1++;
                    }
                }

                if ((chunkRequest.flagsWS & 1) == 0 || (chunkRequest.flagsMain & 2) != 0) {
                    this.pendingRequests1.remove(int2--);
                    ChunkSaveWorker.instance.Update(chunkRequest.chunk);
                    if ((chunkRequest.flagsUDP & 4) != 0) {
                        File file0 = zombie.ChunkMapFilenames.instance.getFilename(chunkRequest.chunk.wx, chunkRequest.chunk.wy);
                        if (file0.exists()) {
                            if (this.NetworkFileDebug) {
                                DebugLog.log(
                                    DebugType.NetworkFileDebug,
                                    "deleting map_" + chunkRequest.chunk.wx + "_" + chunkRequest.chunk.wy + ".bin because it doesn't exist on the server"
                                );
                            }

                            file0.delete();
                            ChunkChecksum.setChecksum(chunkRequest.chunk.wx, chunkRequest.chunk.wy, 0L);
                        }
                    }

                    ByteBuffer byteBuffer = (chunkRequest.flagsWS & 1) != 0 ? null : chunkRequest.bb;
                    if (byteBuffer != null) {
                        try {
                            byteBuffer = this.decompress(byteBuffer);
                        } catch (DataFormatException dataFormatException) {
                            DebugLog.General
                                .error(
                                    "WorldStreamer.loadReceivedChunks: Error while the chunk ("
                                        + chunkRequest.chunk.wx
                                        + ", "
                                        + chunkRequest.chunk.wy
                                        + ") was decompressing"
                                );
                            this.chunkRequests1.add(chunkRequest.chunk);
                            continue;
                        }

                        if (this.bCompare) {
                            File file1 = zombie.ChunkMapFilenames.instance.getFilename(chunkRequest.chunk.wx, chunkRequest.chunk.wy);
                            if (file1.exists()) {
                                this.compare(chunkRequest, byteBuffer, file1);
                            }
                        }
                    }

                    if ((chunkRequest.flagsWS & 8) == 0) {
                        if ((chunkRequest.flagsWS & 1) == 0 && !chunkRequest.chunk.refs.isEmpty()) {
                            if (byteBuffer != null) {
                                byteBuffer.position(0);
                            }

                            this.DoChunk(chunkRequest.chunk, byteBuffer);
                        } else {
                            if (this.NetworkFileDebug) {
                                DebugLog.log(
                                    DebugType.NetworkFileDebug,
                                    chunkRequest.chunk.wx + "_" + chunkRequest.chunk.wy + " refs.isEmpty() SafeWrite=" + (byteBuffer != null)
                                );
                            }

                            if (byteBuffer != null) {
                                long _long = ChunkChecksum.getChecksumIfExists(chunkRequest.chunk.wx, chunkRequest.chunk.wy);
                                this.crc32.reset();
                                this.crc32.update(byteBuffer.array(), 0, byteBuffer.position());
                                if (_long != this.crc32.getValue()) {
                                    ChunkChecksum.setChecksum(chunkRequest.chunk.wx, chunkRequest.chunk.wy, this.crc32.getValue());
                                    IsoChunk.SafeWrite("map_", chunkRequest.chunk.wx, chunkRequest.chunk.wy, byteBuffer);
                                }
                            }

                            chunkRequest.chunk.resetForStore();

                            assert !IsoChunkMap.chunkStore.contains(chunkRequest.chunk);

                            IsoChunkMap.chunkStore.add(chunkRequest.chunk);
                        }
                    }

                    if (chunkRequest.bb != null) {
                        this.releaseBuffer(chunkRequest.bb);
                    }

                    WorldStreamer.ChunkRequest.release(chunkRequest);
                }
            }
        }

        if (_boolean && (int0 != 0 || int1 != 0 || !this.pendingRequests1.isEmpty())) {
            DebugLog.log("nReceived=" + int0 + " nCancel=" + int1 + " nPending=" + this.pendingRequests1.size());
        }
    }

    private ByteBuffer decompress(ByteBuffer byteBuffer) throws DataFormatException {
        this.decompressor.reset();
        this.decompressor.setInput(byteBuffer.array(), 0, byteBuffer.position());
        int int0 = 0;
        if (this.inMemoryZip != null) {
            this.inMemoryZip.clear();
        }

        while (!this.decompressor.finished()) {
            int int1 = this.decompressor.inflate(this.readBuf);
            if (int1 != 0) {
                this.inMemoryZip = this.ensureCapacity(this.inMemoryZip, int0 + int1);
                this.inMemoryZip.put(this.readBuf, 0, int1);
                int0 += int1;
            } else if (!this.decompressor.finished()) {
                throw new DataFormatException();
            }
        }

        this.inMemoryZip.limit(this.inMemoryZip.position());
        return this.inMemoryZip;
    }

    private void threadLoop() throws DataFormatException, InterruptedException, IOException {
        if (GameClient.bClient && !zombie.SystemDisabler.doWorldSyncEnable) {
            this.NetworkFileDebug = DebugType.Do(DebugType.NetworkFileDebug);

            for (IsoChunk isoChunk0 = (IsoChunk)this.chunkRequests0.poll(); isoChunk0 != null; isoChunk0 = (IsoChunk)this.chunkRequests0.poll()) {
                while (isoChunk0 != null) {
                    IsoChunk isoChunk1 = isoChunk0.next;
                    this.chunkRequests1.add(isoChunk0);
                    isoChunk0 = isoChunk1;
                }
            }

            if (!this.chunkRequests1.isEmpty()) {
                comp.init();
                Collections.sort(this.chunkRequests1, comp);
                this.sendRequests();
            }

            this.loadReceivedChunks();
            this.cancelOutOfBoundsRequests();
            this.resendTimedOutRequests();
        }

        for (IsoChunk isoChunk2 = (IsoChunk)this.jobQueue.poll(); isoChunk2 != null; isoChunk2 = (IsoChunk)this.jobQueue.poll()) {
            if (this.jobList.contains(isoChunk2)) {
                DebugLog.log("Ignoring duplicate chunk added to WorldStreamer.jobList");
            } else {
                this.jobList.add(isoChunk2);
            }
        }

        if (this.jobList.isEmpty()) {
            ChunkSaveWorker.instance.Update(null);
            if (ChunkSaveWorker.instance.bSaving) {
                return;
            }

            if (!this.pendingRequests1.isEmpty()) {
                Thread.sleep(20L);
                return;
            }

            Thread.sleep(140L);
        } else {
            for (int _int = this.jobList.size() - 1; _int >= 0; _int--) {
                IsoChunk isoChunk3 = (IsoChunk)this.jobList.get(_int);
                if (isoChunk3.refs.isEmpty()) {
                    this.jobList.remove(_int);
                    isoChunk3.resetForStore();

                    assert !IsoChunkMap.chunkStore.contains(isoChunk3);

                    IsoChunkMap.chunkStore.add(isoChunk3);
                }
            }

            boolean _boolean = !this.jobList.isEmpty();
            IsoChunk isoChunk4 = null;
            if (_boolean) {
                comp.init();
                Collections.sort(this.jobList, comp);
                isoChunk4 = (IsoChunk)this.jobList.remove(this.jobList.size() - 1);
            }

            ChunkSaveWorker.instance.Update(isoChunk4);
            if (isoChunk4 != null) {
                if (isoChunk4.refs.isEmpty()) {
                    isoChunk4.resetForStore();

                    assert !IsoChunkMap.chunkStore.contains(isoChunk4);

                    IsoChunkMap.chunkStore.add(isoChunk4);
                } else {
                    this.DoChunk(isoChunk4, null);
                }
            }

            if (_boolean || ChunkSaveWorker.instance.bSaving) {
                return;
            }
        }

        if (!GameClient.bClient && !zombie.GameWindow.bLoadedAsClient && PlayerDB.isAvailable()) {
            PlayerDB.getInstance().updateWorldStreamer();
        }

        VehiclesDB2.instance.updateWorldStreamer();
        if (IsoPlayer.getInstance() != null) {
            Thread.sleep(140L);
        } else {
            Thread.sleep(0L);
        }
    }

    public void create() {
        if (this.worldStreamer == null) {
            if (!GameServer.bServer) {
                this.bFinished = false;
                this.worldStreamer = new Thread(ThreadGroups.Workers, () -> {
                    while (!this.bFinished) {
                        try {
                            this.threadLoop();
                        } catch (Exception exception) {
                            exception.printStackTrace();
                        }
                    }
                });
                this.worldStreamer.setPriority(5);
                this.worldStreamer.setDaemon(true);
                this.worldStreamer.setName("World Streamer");
                this.worldStreamer.setUncaughtExceptionHandler(zombie.GameWindow::uncaughtException);
                this.worldStreamer.start();
            }
        }
    }

    public void addJob(IsoChunk chunk, int wx, int wy, boolean bDoServerRequest) {
        if (!GameServer.bServer) {
            chunk.wx = wx;
            chunk.wy = wy;
            if (GameClient.bClient && !zombie.SystemDisabler.doWorldSyncEnable && bDoServerRequest) {
                chunk.next = this.chunkHeadMain;
                this.chunkHeadMain = chunk;
            } else {
                assert !this.jobQueue.contains(chunk);

                assert !this.jobList.contains(chunk);

                this.jobQueue.add(chunk);
            }
        }
    }

    public void DoChunk(IsoChunk chunk, ByteBuffer fromServer) {
        if (!GameServer.bServer) {
            this.DoChunkAlways(chunk, fromServer);
        }
    }

    public void DoChunkAlways(IsoChunk chunk, ByteBuffer fromServer) {
        if (Core.bDebug && DebugOptions.instance.WorldStreamerSlowLoad.getValue()) {
            try {
                Thread.sleep(50L);
            } catch (InterruptedException interruptedException) {
            }
        }

        if (chunk != null) {
            try {
                if (!chunk.LoadOrCreate(chunk.wx, chunk.wy, fromServer)) {
                    if (GameClient.bClient) {
                        ChunkChecksum.setChecksum(chunk.wx, chunk.wy, 0L);
                    }

                    chunk.Blam(chunk.wx, chunk.wy);
                    if (!chunk.LoadBrandNew(chunk.wx, chunk.wy)) {
                        return;
                    }
                }

                if (fromServer == null) {
                    VehiclesDB2.instance.loadChunk(chunk);
                }
            } catch (Exception exception0) {
                DebugLog.General.error("Exception thrown while trying to load chunk: " + chunk.wx + ", " + chunk.wy);
                exception0.printStackTrace();
                if (GameClient.bClient) {
                    ChunkChecksum.setChecksum(chunk.wx, chunk.wy, 0L);
                }

                chunk.Blam(chunk.wx, chunk.wy);
                if (!chunk.LoadBrandNew(chunk.wx, chunk.wy)) {
                    return;
                }
            }

            if (chunk.jobType != IsoChunk.JobType.Convert && chunk.jobType != IsoChunk.JobType.SoftReset) {
                try {
                    if (!chunk.refs.isEmpty()) {
                        chunk.loadInWorldStreamerThread();
                    }
                } catch (Exception exception1) {
                    exception1.printStackTrace();
                }

                IsoChunk.loadGridSquare.add(chunk);
            } else {
                chunk.doLoadGridsquare();
                chunk.bLoaded = true;
            }
        }
    }

    public void addJobInstant(IsoChunk chunk, int x, int y, int wx, int wy) {
        if (!GameServer.bServer) {
            chunk.wx = wx;
            chunk.wy = wy;

            try {
                this.DoChunkAlways(chunk, null);
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
    }

    public void addJobConvert(IsoChunk chunk, int x, int y, int wx, int wy) {
        if (!GameServer.bServer) {
            chunk.wx = wx;
            chunk.wy = wy;
            chunk.jobType = IsoChunk.JobType.Convert;

            try {
                this.DoChunk(chunk, null);
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
    }

    public void addJobWipe(IsoChunk chunk, int x, int y, int wx, int wy) {
        chunk.wx = wx;
        chunk.wy = wy;
        chunk.jobType = IsoChunk.JobType.SoftReset;

        try {
            this.DoChunkAlways(chunk, null);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    public boolean isBusy() {
        return !GameClient.bClient
                || this.chunkRequests0.isEmpty()
                    && this.chunkRequests1.isEmpty()
                    && this.chunkHeadMain == null
                    && this.waitingToSendQ.isEmpty()
                    && this.waitingToCancelQ.isEmpty()
                    && this.sentRequests.isEmpty()
                    && this.pendingRequests.isEmpty()
                    && this.pendingRequests1.isEmpty()
            ? !this.jobQueue.isEmpty() || !this.jobList.isEmpty()
            : true;
    }

    public void stop() {
        DebugLog.log("EXITDEBUG: WorldStreamer.stop 1");
        if (this.worldStreamer != null) {
            this.bFinished = true;
            DebugLog.log("EXITDEBUG: WorldStreamer.stop 2");

            while (this.worldStreamer.isAlive()) {
            }

            DebugLog.log("EXITDEBUG: WorldStreamer.stop 3");
            this.worldStreamer = null;
            this.jobList.clear();
            this.jobQueue.clear();
            DebugLog.log("EXITDEBUG: WorldStreamer.stop 4");
            ChunkSaveWorker.instance.SaveNow();
            ChunkChecksum.Reset();
            DebugLog.log("EXITDEBUG: WorldStreamer.stop 5");
        }
    }

    public void quit() {
        this.stop();
    }

    public void requestLargeAreaZip(int wx, int wy, int range) throws IOException {
        ByteBufferWriter byteBufferWriter = GameClient.connection.startPacket();
        PacketTypes.PacketType.RequestLargeAreaZip.doPacket(byteBufferWriter);
        byteBufferWriter.putInt(wx);
        byteBufferWriter.putInt(wy);
        byteBufferWriter.putInt(IsoChunkMap.ChunkGridWidth);
        PacketTypes.PacketType.RequestLargeAreaZip.send(GameClient.connection);
        this.requestingLargeArea = true;
        this.largeAreaDownloads = 0;
        GameLoadingState.GameLoadingString = Translator.getText("IGUI_MP_RequestMapData");
        int int0 = 0;
        int int1 = wx - range;
        int int2 = wy - range;
        int int3 = wx + range;
        int int4 = wy + range;

        for (int int5 = int2; int5 <= int4; int5++) {
            for (int int6 = int1; int6 <= int3; int6++) {
                if (IsoWorld.instance.MetaGrid.isValidChunk(int6, int5)) {
                    IsoChunk isoChunk = (IsoChunk)IsoChunkMap.chunkStore.poll();
                    if (isoChunk == null) {
                        isoChunk = new IsoChunk(IsoWorld.instance.CurrentCell);
                    } else {
                        MPStatistics.decreaseStoredChunk();
                    }

                    this.addJob(isoChunk, int6, int5, true);
                    int0++;
                }
            }
        }

        DebugLog.log("Requested " + int0 + " chunks from the server");
        long long0 = System.currentTimeMillis();
        long long1 = long0;
        int int7 = 0;
        int int8 = 0;

        while (this.isBusy()) {
            long long2 = System.currentTimeMillis();
            if (long2 - long1 > 60000L) {
                GameLoadingState.mapDownloadFailed = true;
                throw new IOException("map download from server timed out");
            }

            int int9 = this.largeAreaDownloads;
            GameLoadingState.GameLoadingString = Translator.getText("IGUI_MP_DownloadedMapData", int9, int0);
            long long3 = long2 - long0;
            if (long3 / 1000L > (long)int7) {
                DebugLog.log("Received " + int9 + " / " + int0 + " chunks");
                int7 = (int)(long3 / 1000L);
            }

            if (int8 < int9) {
                long1 = long2;
                int8 = int9;
            }

            try {
                Thread.sleep(100L);
            } catch (InterruptedException interruptedException) {
            }
        }

        DebugLog.log("Received " + this.largeAreaDownloads + " / " + int0 + " chunks");
        this.requestingLargeArea = false;
    }

    private void cancelOutOfBoundsRequests() {
        if (!this.requestingLargeArea) {
            for (int _int = 0; _int < this.pendingRequests1.size(); _int++) {
                WorldStreamer.ChunkRequest chunkRequest = (WorldStreamer.ChunkRequest)this.pendingRequests1.get(_int);
                if ((chunkRequest.flagsWS & 1) == 0 && chunkRequest.chunk.refs.isEmpty()) {
                    chunkRequest.flagsWS |= 1;
                    this.waitingToCancelQ.add(chunkRequest);
                }
            }
        }
    }

    private void resendTimedOutRequests() {
        long _long = System.currentTimeMillis();

        for (int _int = 0; _int < this.pendingRequests1.size(); _int++) {
            WorldStreamer.ChunkRequest chunkRequest = (WorldStreamer.ChunkRequest)this.pendingRequests1.get(_int);
            if ((chunkRequest.flagsWS & 1) == 0 && chunkRequest.time + 8000L < _long) {
                if (this.NetworkFileDebug) {
                    DebugLog.log(DebugType.NetworkFileDebug, "chunk request timed out " + chunkRequest.chunk.wx + "," + chunkRequest.chunk.wy);
                }

                this.chunkRequests1.add(chunkRequest.chunk);
                chunkRequest.flagsWS |= 9;
                chunkRequest.flagsMain |= 2;
            }
        }
    }

    public void receiveChunkPart(ByteBuffer bb) {
        for (WorldStreamer.ChunkRequest chunkRequest0 = (WorldStreamer.ChunkRequest)this.sentRequests.poll();
            chunkRequest0 != null;
            chunkRequest0 = (WorldStreamer.ChunkRequest)this.sentRequests.poll()
        ) {
            this.pendingRequests.add(chunkRequest0);
        }

        int int0 = bb.getInt();
        int int1 = bb.getInt();
        int int2 = bb.getInt();
        int int3 = bb.getInt();
        int int4 = bb.getInt();
        int int5 = bb.getInt();

        for (int int6 = 0; int6 < this.pendingRequests.size(); int6++) {
            WorldStreamer.ChunkRequest chunkRequest1 = (WorldStreamer.ChunkRequest)this.pendingRequests.get(int6);
            if ((chunkRequest1.flagsWS & 1) != 0) {
                this.pendingRequests.remove(int6--);
                chunkRequest1.flagsUDP |= 16;
            } else if (chunkRequest1.requestNumber == int0) {
                if (chunkRequest1.bb == null) {
                    chunkRequest1.bb = this.getByteBuffer(int3);
                }

                System.arraycopy(bb.array(), bb.position(), chunkRequest1.bb.array(), int4, int5);
                if (chunkRequest1.partsReceived == null) {
                    chunkRequest1.partsReceived = new boolean[int1];
                }

                chunkRequest1.partsReceived[int2] = true;
                if (chunkRequest1.isReceived()) {
                    if (this.NetworkFileDebug) {
                        DebugLog.log(DebugType.NetworkFileDebug, "received all parts for " + chunkRequest1.chunk.wx + "," + chunkRequest1.chunk.wy);
                    }

                    chunkRequest1.bb.position(int3);
                    this.pendingRequests.remove(int6);
                    chunkRequest1.flagsUDP |= 16;
                    if (this.requestingLargeArea) {
                        this.largeAreaDownloads++;
                    }
                }
                break;
            }
        }
    }

    public void receiveNotRequired(ByteBuffer bb) {
        for (WorldStreamer.ChunkRequest chunkRequest0 = (WorldStreamer.ChunkRequest)this.sentRequests.poll();
            chunkRequest0 != null;
            chunkRequest0 = (WorldStreamer.ChunkRequest)this.sentRequests.poll()
        ) {
            this.pendingRequests.add(chunkRequest0);
        }

        int int0 = bb.getInt();

        for (int int1 = 0; int1 < int0; int1++) {
            int int2 = bb.getInt();
            boolean _boolean = bb.get() == 1;

            for (int int3 = 0; int3 < this.pendingRequests.size(); int3++) {
                WorldStreamer.ChunkRequest chunkRequest1 = (WorldStreamer.ChunkRequest)this.pendingRequests.get(int3);
                if ((chunkRequest1.flagsWS & 1) != 0) {
                    this.pendingRequests.remove(int3--);
                    chunkRequest1.flagsUDP |= 16;
                } else if (chunkRequest1.requestNumber == int2) {
                    if (this.NetworkFileDebug) {
                        DebugLog.log(
                            DebugType.NetworkFileDebug, "NotRequiredInZip " + chunkRequest1.chunk.wx + "," + chunkRequest1.chunk.wy + " delete=" + !_boolean
                        );
                    }

                    if (!_boolean) {
                        chunkRequest1.flagsUDP |= 4;
                    }

                    this.pendingRequests.remove(int3);
                    chunkRequest1.flagsUDP |= 16;
                    if (this.requestingLargeArea) {
                        this.largeAreaDownloads++;
                    }
                    break;
                }
            }
        }
    }

    private void compare(WorldStreamer.ChunkRequest chunkRequest, ByteBuffer byteBuffer, File file) throws IOException {
        IsoChunk isoChunk0 = (IsoChunk)IsoChunkMap.chunkStore.poll();
        if (isoChunk0 == null) {
            isoChunk0 = new IsoChunk(IsoWorld.instance.getCell());
        } else {
            MPStatistics.decreaseStoredChunk();
        }

        isoChunk0.wx = chunkRequest.chunk.wx;
        isoChunk0.wy = chunkRequest.chunk.wy;
        IsoChunk isoChunk1 = (IsoChunk)IsoChunkMap.chunkStore.poll();
        if (isoChunk1 == null) {
            isoChunk1 = new IsoChunk(IsoWorld.instance.getCell());
        } else {
            MPStatistics.decreaseStoredChunk();
        }

        isoChunk1.wx = chunkRequest.chunk.wx;
        isoChunk1.wy = chunkRequest.chunk.wy;
        int _int = byteBuffer.position();
        byteBuffer.position(0);
        isoChunk0.LoadFromBuffer(chunkRequest.chunk.wx, chunkRequest.chunk.wy, byteBuffer);
        byteBuffer.position(_int);
        this.crc32.reset();
        this.crc32.update(byteBuffer.array(), 0, _int);
        DebugLog.log(
            "downloaded crc=" + this.crc32.getValue() + " on-disk crc=" + ChunkChecksum.getChecksumIfExists(chunkRequest.chunk.wx, chunkRequest.chunk.wy)
        );
        isoChunk1.LoadFromDisk();
        DebugLog.log("downloaded size=" + _int + " on-disk size=" + file.length());
        this.compareChunks(isoChunk0, isoChunk1);
        isoChunk0.resetForStore();

        assert !IsoChunkMap.chunkStore.contains(isoChunk0);

        IsoChunkMap.chunkStore.add(isoChunk0);
        isoChunk1.resetForStore();

        assert !IsoChunkMap.chunkStore.contains(isoChunk1);

        IsoChunkMap.chunkStore.add(isoChunk1);
    }

    private void compareChunks(IsoChunk isoChunk0, IsoChunk isoChunk1) {
        DebugLog.log("comparing " + isoChunk0.wx + "," + isoChunk0.wy);

        try {
            this.compareErosion(isoChunk0, isoChunk1);
            if (isoChunk0.lootRespawnHour != isoChunk1.lootRespawnHour) {
                DebugLog.log("lootRespawnHour " + isoChunk0.lootRespawnHour + " != " + isoChunk1.lootRespawnHour);
            }

            for (int int0 = 0; int0 < 10; int0++) {
                for (int int1 = 0; int1 < 10; int1++) {
                    IsoGridSquare isoGridSquare0 = isoChunk0.getGridSquare(int1, int0, 0);
                    IsoGridSquare isoGridSquare1 = isoChunk1.getGridSquare(int1, int0, 0);
                    this.compareSquares(isoGridSquare0, isoGridSquare1);
                }
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    private void compareErosion(IsoChunk isoChunk1, IsoChunk isoChunk0) {
        if (isoChunk1.getErosionData().init != isoChunk0.getErosionData().init) {
            DebugLog.log("init " + isoChunk1.getErosionData().init + " != " + isoChunk0.getErosionData().init);
        }

        if (isoChunk1.getErosionData().eTickStamp != isoChunk0.getErosionData().eTickStamp) {
            DebugLog.log("eTickStamp " + isoChunk1.getErosionData().eTickStamp + " != " + isoChunk0.getErosionData().eTickStamp);
        }

        if (isoChunk1.getErosionData().moisture != isoChunk0.getErosionData().moisture) {
            DebugLog.log("moisture " + isoChunk1.getErosionData().moisture + " != " + isoChunk0.getErosionData().moisture);
        }

        if (isoChunk1.getErosionData().minerals != isoChunk0.getErosionData().minerals) {
            DebugLog.log("minerals " + isoChunk1.getErosionData().minerals + " != " + isoChunk0.getErosionData().minerals);
        }

        if (isoChunk1.getErosionData().epoch != isoChunk0.getErosionData().epoch) {
            DebugLog.log("epoch " + isoChunk1.getErosionData().epoch + " != " + isoChunk0.getErosionData().epoch);
        }

        if (isoChunk1.getErosionData().soil != isoChunk0.getErosionData().soil) {
            DebugLog.log("soil " + isoChunk1.getErosionData().soil + " != " + isoChunk0.getErosionData().soil);
        }
    }

    private void compareSquares(IsoGridSquare isoGridSquare1, IsoGridSquare isoGridSquare0) {
        if (isoGridSquare1 != null && isoGridSquare0 != null) {
            try {
                this.bb1.clear();
                isoGridSquare1.save(this.bb1, null);
                this.bb1.flip();
                this.bb2.clear();
                isoGridSquare0.save(this.bb2, null);
                this.bb2.flip();
                if (this.bb1.compareTo(this.bb2) != 0) {
                    boolean _boolean = true;
                    int int0 = -1;
                    if (this.bb1.limit() == this.bb2.limit()) {
                        for (int int1 = 0; int1 < this.bb1.limit(); int1++) {
                            if (this.bb1.get(int1) != this.bb2.get(int1)) {
                                int0 = int1;
                                break;
                            }
                        }

                        for (int int2 = 0; int2 < isoGridSquare1.getErosionData().regions.size(); int2++) {
                            if (((ErosionCategory.Data)isoGridSquare1.getErosionData().regions.get(int2)).dispSeason
                                != ((ErosionCategory.Data)isoGridSquare0.getErosionData().regions.get(int2)).dispSeason) {
                                DebugLog.log(
                                    "season1="
                                        + ((ErosionCategory.Data)isoGridSquare1.getErosionData().regions.get(int2)).dispSeason
                                        + " season2="
                                        + ((ErosionCategory.Data)isoGridSquare0.getErosionData().regions.get(int2)).dispSeason
                                );
                                _boolean = false;
                            }
                        }
                    }

                    DebugLog.log(
                        "square "
                            + isoGridSquare1.x
                            + ","
                            + isoGridSquare1.y
                            + " mismatch at "
                            + int0
                            + " seasonMatch="
                            + _boolean
                            + " #regions="
                            + isoGridSquare1.getErosionData().regions.size()
                    );
                    if (isoGridSquare1.getObjects().size() == isoGridSquare0.getObjects().size()) {
                        for (int int3 = 0; int3 < isoGridSquare1.getObjects().size(); int3++) {
                            IsoObject isoObject0 = isoGridSquare1.getObjects().get(int3);
                            IsoObject isoObject1 = isoGridSquare0.getObjects().get(int3);
                            this.bb1.clear();
                            isoObject0.save(this.bb1);
                            this.bb1.flip();
                            this.bb2.clear();
                            isoObject1.save(this.bb2);
                            this.bb2.flip();
                            if (this.bb1.compareTo(this.bb2) != 0) {
                                DebugLog.log(
                                    "  1: "
                                        + isoObject0.getClass().getName()
                                        + " "
                                        + isoObject0.getName()
                                        + " "
                                        + (isoObject0.sprite == null ? "no sprite" : isoObject0.sprite.name)
                                );
                                DebugLog.log(
                                    "  2: "
                                        + isoObject1.getClass().getName()
                                        + " "
                                        + isoObject1.getName()
                                        + " "
                                        + (isoObject1.sprite == null ? "no sprite" : isoObject1.sprite.name)
                                );
                            }
                        }
                    } else {
                        for (int int4 = 0; int4 < isoGridSquare1.getObjects().size(); int4++) {
                            IsoObject isoObject2 = isoGridSquare1.getObjects().get(int4);
                            DebugLog.log(
                                "  "
                                    + isoObject2.getClass().getName()
                                    + " "
                                    + isoObject2.getName()
                                    + " "
                                    + (isoObject2.sprite == null ? "no sprite" : isoObject2.sprite.name)
                            );
                        }
                    }
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        } else {
            if (isoGridSquare1 != null || isoGridSquare0 != null) {
                DebugLog.log("one square is null, the other isn't");
            }
        }
    }

    private static class ChunkComparator implements Comparator<IsoChunk> {
        private Vector2[] pos = new Vector2[4];

        public ChunkComparator() {
            for (int _int = 0; _int < 4; _int++) {
                this.pos[_int] = new Vector2();
            }
        }

        public void init() {
            for (int _int = 0; _int < 4; _int++) {
                Vector2 vector2 = this.pos[_int];
                vector2.x = vector2.y = -1.0F;
                IsoPlayer isoPlayer = IsoPlayer.players[_int];
                if (isoPlayer != null) {
                    if (isoPlayer.lx == isoPlayer.x && isoPlayer.ly == isoPlayer.y) {
                        vector2.x = isoPlayer.x;
                        vector2.y = isoPlayer.y;
                    } else {
                        vector2.x = isoPlayer.x - isoPlayer.lx;
                        vector2.y = isoPlayer.y - isoPlayer.ly;
                        vector2.normalize();
                        vector2.setLength(10.0F);
                        vector2.x = vector2.x + isoPlayer.x;
                        vector2.y = vector2.y + isoPlayer.y;
                    }
                }
            }
        }

        public int compare(IsoChunk isoChunk0, IsoChunk isoChunk1) {
            float float0 = Float.MAX_VALUE;
            float float1 = Float.MAX_VALUE;

            for (int _int = 0; _int < 4; _int++) {
                if (this.pos[_int].x != -1.0F || this.pos[_int].y != -1.0F) {
                    float float2 = this.pos[_int].x;
                    float float3 = this.pos[_int].y;
                    float0 = Math.min(float0, IsoUtils.DistanceTo(float2, float3, (float)(isoChunk0.wx * 10 + 5), (float)(isoChunk0.wy * 10 + 5)));
                    float1 = Math.min(float1, IsoUtils.DistanceTo(float2, float3, (float)(isoChunk1.wx * 10 + 5), (float)(isoChunk1.wy * 10 + 5)));
                }
            }

            if (float0 < float1) {
                return 1;
            } else {
                return float0 > float1 ? -1 : 0;
            }
        }
    }

    private static final class ChunkRequest {
        static final ArrayDeque<WorldStreamer.ChunkRequest> pool = new ArrayDeque();
        IsoChunk chunk;
        int requestNumber;
        boolean[] partsReceived = null;
        long crc;
        ByteBuffer bb;
        transient int flagsMain;
        transient int flagsUDP;
        transient int flagsWS;
        long time;
        WorldStreamer.ChunkRequest next;

        boolean isReceived() {
            if (this.partsReceived == null) {
                return false;
            } else {
                for (int _int = 0; _int < this.partsReceived.length; _int++) {
                    if (!this.partsReceived[_int]) {
                        return false;
                    }
                }

                return true;
            }
        }

        static WorldStreamer.ChunkRequest alloc() {
            return pool.isEmpty() ? new WorldStreamer.ChunkRequest() : (WorldStreamer.ChunkRequest)pool.pop();
        }

        static void release(WorldStreamer.ChunkRequest chunkRequest) {
            chunkRequest.chunk = null;
            chunkRequest.partsReceived = null;
            chunkRequest.bb = null;
            chunkRequest.flagsMain = 0;
            chunkRequest.flagsUDP = 0;
            chunkRequest.flagsWS = 0;
            pool.push(chunkRequest);
        }
    }
}
